﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Json;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Threading;
using AppReviewerClient.Component;
using AppReviewerClient.ExerciseRepository;
using Caliburn.Micro;
using Microsoft.Expression.Encoder;
using Microsoft.Expression.Encoder.Devices;
using Microsoft.Expression.Encoder.Profiles;
using Microsoft.Expression.Encoder.ScreenCapture;
using MvcApplication2.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Application = System.Windows.Application;
using System.ComponentModel.Composition;
using Action = System.Action;


namespace AppReviewerClient
{

    /// <summary>
    /// Feature :
    /// - Login credenziali.
    /// 
    /// - Elenco esercizi disponibili, ( sono stati contrattati online )
    /// 
    /// - Invio risultati
    ///     - screencast, risposte , note.
    /// 
    /// - Il richiedente sarà avvisato del termine del quest , ma potrà accedere ai dati solo previo pagamento.
    /// 
    /// -> una volta fatto upload verrà reso come disponibili e visualizzabile solo dopo aver pagato )
    /// 
    /// vediamo come registrare .
    /// 
    /// lista esercizi.
    /// comincia.
    /// selezione esercizio. -> webcam / screencapture->
    /// accensione manuale webcam / screencapture.
    /// fine esercizio.
    /// recording-> (limit) max 10 min ->
    /// ->encode -> upload -> once upload -> esercizio fatto -> pagamento -> scaricabile.
    /// 
    /// stato dell'esercizio l'esercizio 
    /// chiave univoco dell'esercizio.
    /// 
    /// scarico lista scaricati.
    /// salvo
    /// l'esercizio da parte posso fermarmi e riprendere.
    /// rimane legato con id.
    ///
    /// scarico lista da internet.
    /// sul pc ho salvato parziali.
    /// legati da id.
    /// li salvo dentro cartella.
    /// devo memorizzare anche lo stato - OK.
    /// ------------------------------------
    /// 
    /// Un'altra cosa da fare è :
    /// - lista encoding.
    /// - lista upload.
    /// 
    /// Voglio fare un'upload alla volta e un'encoding .
    /// Queste liste devono essere salvate in file.
    /// Senza scomodare troppa roba.
    /// Voglio creare lista di upload. encoding. e creo flag.
    /// Lo tengo comunque in lista cosi che se interrompo l'operazione rimane comunque da fare.
    ///  Su start encoding lo aggiungo alla lista degli upload.
    /// posso mettere in pausa sia encoding che upload.
    /// 
    /// MONOUTENTE
    /// . per farmi le cose più semplici non penso a più utenti su stesso pc.
    ///
    /// </summary>
    [Export(typeof(IShell))]
    public class ShellViewModel : ViewAware, IShell
    {
        private readonly IWindowManager _windowManager;

        // private readonly IExerciseRepository _exerciseRepository;

        private readonly ScreenCaptureJob _job = new ScreenCaptureJob();

        private readonly QueueWorkList _queueWorkList;

        private string _displayUsername;
        public string DisplayUsername
        {
            get { return _displayUsername; }
            set
            {
                _displayUsername = value;
                NotifyOfPropertyChange(() => DisplayUsername);
            }
        }
        private string _btnLogInText;
        public string BtnLogInText
        {
            get { return _btnLogInText; }
            set
            {
                _btnLogInText = value;
                NotifyOfPropertyChange(() => BtnLogInText);
            }
        }

        private VideoEncoder _videoEncoder;
        public VideoEncoder VideoEncoder
        {
            get { return _videoEncoder; }
            set
            {
                _videoEncoder = value;
                NotifyOfPropertyChange(() => VideoEncoder);
            }
        }
        public LoginViewModel LoginViewModel;

        public YouTubeLoginViewModel YouTubeLoginViewModel;

        private YouTubeUploader _tubeUploader;
        public YouTubeUploader YouTubeUploader
        {
            get { return _tubeUploader; }
            set
            {
                _tubeUploader = value;
                NotifyOfPropertyChange(() => YouTubeUploader);
            }
        }

        public const string Host = "http://localhost:58071/";

        private SiteCommunicator _siteCommunicator = new SiteCommunicator();
        ///// <summary>
        ///// Youtube username
        ///// </summary>
        //private string _u;

        ///// <summary>
        ///// youtube password
        ///// </summary>
        //private string _p;

        // public ViewModels.CredentialsViewModel CredentialsViewModel { get; private set; }

        [ImportingConstructor]
        public ShellViewModel(IWindowManager windowManager)
        {
            _windowManager = windowManager;

            //  YouTubeUploader = new YouTubeUploader();

            //sarebbe meglio averla tramite microcaliburn..
            //  _exerciseRepository = new DummyExerciseRepository();

            YouTubeUploader = new YouTubeUploader();


            _queueWorkList = FilepathHelper.GetFileWorkList();

            YouTubeUploader.OnUploadCompleted += YouTubeUploader_OnUploadCompleted;

            //CredentialsViewModel = PathFolderHelper.GetCredentials();

            //StartUploadVideo("webcam.png","user6", "localhost");
        }


        /// Uploader ha finito di fare upload , chiedo prossimo file
        /// .anche qui richjhiamarlo con dispachter dell'applicazioen.
        void YouTubeUploader_OnUploadCompleted(object myObject, YouTubeUploader.UploadArgs myArgs)
        {
            var uploadItem = myArgs.UploadItem;
            // Set video upload to internet database

           // _siteCommunicator.SetExerciseStatus("completed",uploadItem.VideoId);
            //Delete Video
            FilepathHelper.DeleteVideo(uploadItem.Filepath);

            //DeleteVideo();

            TryStartUploading();
        }

        protected override void OnViewLoaded(object view)
        {
            base.OnViewLoaded(view);

            LoginViewModel = new LoginViewModel(this);

            YouTubeLoginViewModel = new YouTubeLoginViewModel(this);

            VideoEncoder = new VideoEncoder();

            Login();

            TryStartUploading();
        }

        public void Login()
        {
            var isUserNameInserted = LoginViewModel.CredentialsValid;

            if (!isUserNameInserted.HasValue || isUserNameInserted.Value == false)
            {
                BtnLogInText = "Log In";

                DisplayUsername = string.Empty;

                _windowManager.ShowDialog(LoginViewModel);
            }
            else
            {
                DisplayUsername = LoginViewModel.Username;

                BtnLogInText = "Log Off";

                OnLogged();
            }
        }

        /// <summary>
        /// Metodo richiamato dopo che utente si è loggato
        /// </summary>
        /// <returns></returns>
        public bool OnLogged()
        {
            YouTubeUploader.ContinuePreviousUploading();
            
        }

        public void LoginYouTube()
        {
            _windowManager.ShowDialog(YouTubeLoginViewModel);
        }
        // Se non c'è login automatico, viene mostrato solo login, o comunque tutto disabilitato.

        // Se loggato ho possibilità di scegliere esercizio o continuare uno cominciato.
        // choose esercizio.( nuova finestra ( apre finestra andando a prendere esercizi non terminati. sempre con http client . continua esercizio , ( ci possono essere + esercizi in sospeso ).
        // richiesta di login , 
        // esercizi disponibili..
        // continura esercizio.

        public void OnInitialized(EventArgs e)
        {
            var screenWidth = SystemParameters.WorkArea.Width;
            var height = SystemParameters.WorkArea.Height;

            // assumo solo orizzontale
            var progWidth = (int)screenWidth;

            var browserWidth = (int)screenWidth - progWidth;

            //if (resizeType != "3")
            //    SetWindowPos(handle, IntPtr.Zero, (int)progWidth, 0, (int)browserWidth, (int)height, SWP_SHOWWINDOW);

            Application.Current.MainWindow.Left = 0;
            Application.Current.MainWindow.Top = 0;
            Application.Current.MainWindow.WindowState = WindowState.Normal;
            Application.Current.MainWindow.Width = screenWidth;
            Application.Current.MainWindow.Height = 150;
            Application.Current.MainWindow.Focus();
        }

        /// <summary>
        /// Mostra nuova finestra con esercizi disponibili.
        /// Sia nuovi (scaricato elenco dal sito tramite http get , che quelli iniziati raccolti dal pc )
        /// </summary>
        public void ShowAvailableExercise()
        {
            var l = new AvailableExerciseViewModel(this);

            _windowManager.ShowDialog(l);
        }

        public void ShowLoginWindow()
        {
            _windowManager.ShowDialog(LoginViewModel);
        }

        public void LogoffCmd()
        {
            LoginViewModel.LogOffCmd();

            BtnLogInText = "Log Off";

            DisplayUsername = string.Empty;

            _windowManager.ShowDialog(LoginViewModel);


        }


        private ExerciseViewModel _selectedExercise;
        public ExerciseViewModel SelectedExercise
        {
            get
            {
                return _selectedExercise;
            }
            set
            {
                _selectedExercise = value;
                NotifyOfPropertyChange(() => SelectedExercise);
                NotifyOfPropertyChange(() => IsExerciseSelected);
            }
        }

        /// <summary>
        /// Chiude esercizio, (non è terminato ) 
        /// setta selected to null
        /// </summary>
        public void CloseExercise()
        {
            var messageBox = System.Windows.MessageBox.Show(@"Chiudere esercizio ?" +
                                                            "Sara possibile continuarlo successivamente", "Close Exercise", MessageBoxButton.OKCancel,
                                                MessageBoxImage.Question);

            if (messageBox != MessageBoxResult.OK) return;

            PauseRecording();
            StopRecording();

            SelectedExercise = null;
        }
        public bool IsExerciseSelected
        {
            get
            {
#if DEBUG
                return true;
#endif
                return SelectedExercise != null;
            }
        }
        #region Recording


        public bool CanStartRecording
        {
            get { return _job.Status != RecordStatus.Running; }
        }
        /// <summary>
        /// Rinominare in StartExercise..
        /// </summary>
        public void StartRecording()
        {
            var exerciseId = 0;

#if !DEBUG
            if (SelectedExercise == null) return;
            exerciseId = SelectedExercise.ExerciseId;
            /*
             * Setta esercizio come cominciato
             */
            PathFolderHelper.UpdateStatus(SelectedExercise, LocalExerciseStatus.Beginned);
#endif


            if (_job.Status == RecordStatus.Paused)
                _job.Resume();
            else
            {
                //var lstVideoDevices = new List<string>();
                //var lstAudioDevices = new List<string>();
                //foreach (EncoderDevice edv in EncoderDevices.FindDevices(EncoderDeviceType.Video))
                //{
                //    lstVideoDevices.Add(edv.Name);
                //}
                //foreach (EncoderDevice eda in EncoderDevices.FindDevices(EncoderDeviceType.Audio))
                //{

                //    lstAudioDevices.Add(eda.Name);
                //}

                System.Drawing.Size monitorSize = SystemInformation.PrimaryMonitorSize;
                var capRect = new System.Drawing.Rectangle(0, 0, monitorSize.Width, monitorSize.Height);


                var audioRecorder =
                    EncoderDevices.FindDevices(EncoderDeviceType.Audio).FirstOrDefault(a => a.Category == EncoderDeviceCategory.Capture);
                if (audioRecorder != null)
                {
                    _job.AddAudioDeviceSource(audioRecorder);
                }
                else
                {
                    throw new Exception("no-mic");
                }

                _job.CaptureLargeMouseCursor = true;
                _job.CaptureMouseCursor = true;
                _job.CaptureRectangle = capRect;
                _job.OutputPath = FilepathHelper.GetTempDirectoryPath(exerciseId);
                _job.Start();
            }

            NotifyOfPropertyChange(() => CanPauseRecording);
            NotifyOfPropertyChange(() => CanStartRecording);
            NotifyOfPropertyChange(() => CanExerciseTermined);


        }

        public bool CanExerciseTermined
        {
            get { return _job.Status != RecordStatus.NotStarted; }
        }

        /// <summary>
        /// Una volta terminato esercizio , procede come segue :
        /// - Fa un merge dei vari video , encode in wmv. In background con barra di avanzamento
        /// - Una volta fatto il merge con successo >>
        /// - Fa upload con credenziali utente in cartella su host.
        /// - Segna tramite WebApiHelper esercizio eseguito
        /// - Rimuove esercizio dalla lista, c'è comunque uno storico su spazio internet.
        /// </summary>
        public void ExerciseCompleted()
        {
            var messageBox = System.Windows.MessageBox.Show("Terminare esercizio ? bla bla bla", "Esercizio completato", MessageBoxButton.OKCancel,
                                                            MessageBoxImage.Question);

            if (messageBox != MessageBoxResult.OK) return;

            /*
             * Per chiarezza una volta terminato esercizio, bisogna impedire di fare ulteriori registrazioni
             * per esercizio.
             * 
             * Magari mettendo flag. ma dove lo salvo ?
             * Magari serializzare sempre come file temporaneo
             * includendo le proprieta come stato avanzamento
             * 
             * cosi diciamo che non ho connessione , mi rimane memorizzato che questo esercizio è da fare encoding e fare upload
             * 
             * Si dovrebbe fare un encoding e upload alla volta, cercare di fare una coda.
             * 
             */
            PauseRecording();
            StopRecording();

            var exerciseId = 0;
#if !DEBUG
            exerciseId = SelectedExercise.ExerciseId;
#endif
            AddToEncoding(exerciseId);

            //Encoding various file into 1 file

            //Upload

            //Set exercise termined
        }

        /// <summary>
        /// Aggiunge esercizio in lista per l'encoding
        /// </summary>
        private void AddToEncoding(int exID)
        {
            _queueWorkList.AddEncoding(exID);

            TryStartEncoding();
        }

        private void TryStartEncoding()
        {
            if (IsEncoding) return;

            var path = _queueWorkList.GetNextEncoding();

            StartEncoding(path);
        }
        /*
         * quando finesce encoding richiamo questo metodo.
         * ma lo devo fare con tread corrent.
         */
        private void TryStartUploading()
        {
            var exID = 0;
#if !DEBUG
            if (YouTubeUploader.CurrentUploadItem == null) return;

            


            /* se già sta facendo uploading, skippa.*/
            exID = _queueWorkList.GetNextUploading();
#endif
            var credentialsCorrect = this.YouTubeUploader.CredentialsCorrect;

            if (!credentialsCorrect)
                LoginYouTube();

            var filepath = FilepathHelper.GetTempDirectoryPathResult(exID);

            var files = Directory.GetFiles(filepath, "*.wmv", SearchOption.AllDirectories);
            /* Prendo tutti i file con estensione .wmv dentro la cartella
             */
            var file = files.FirstOrDefault();

            if (file != null)
                StartUploadVideo(file);
        }



        public void StopRecording()
        {
            _job.Stop();
        }

        public bool CanPauseRecording
        {
            get { return _job.Status == RecordStatus.Running; }
        }
        public void PauseRecording()
        {
            _job.Pause();
            NotifyOfPropertyChange(() => CanPauseRecording);
            NotifyOfPropertyChange(() => CanStartRecording);

        }
        #endregion

        /// <summary>
        /// Lancia esercizio
        /// </summary>
        /// <param name="exercise"></param>
        internal void SetActiveExercise(ExerciseViewModel exercise)
        {
            SelectedExercise = exercise;
            /*
             * Parto da quest non completata.
             */
        }




        #region Encoding

        /// <summary>
        /// Indicates if the encode should be cancelled.
        /// </summary>
        private bool cancelEncode;

        /// <summary>
        /// Seperate Encode Thread.
        /// </summary>
        private Thread _thread;

        /// <summary>
        /// Indicates if we are busy or not.
        /// </summary>
        private bool _isEncoding;
        public bool IsEncoding
        {
            get { return _isEncoding; }
            set
            {
                _isEncoding = value;
                NotifyOfPropertyChange(() => IsEncoding);
            }
        }

        ///// <summary>
        ///// The filename of the file we wish to encode.
        ///// </summary>
        //private string fileName;
        //public string FileName
        //{
        //    get { return fileName; }
        //    set { fileName = value; onPropChanged("FileName"); }
        //}

        /// <summary>
        /// Gets the current status of the encode.
        /// </summary>
        private string _currentEncodingFileName;
        public string CurrentEncodingFileName
        {
            get { return _currentEncodingFileName; }
            set { _currentEncodingFileName = value; NotifyOfPropertyChange(() => CurrentEncodingFileName); }
        }

        /// <summary>
        /// Gets the current encode progress
        /// </summary>
        private double _encodingProgress;
        public double EncodingProgress
        {
            get { return _encodingProgress; }
            set
            {
                _encodingProgress = value;
                NotifyOfPropertyChange(() => EncodingProgress);
            }
        }



        /// <summary>
        /// Starts encoding in seperate thread
        /// </summary>
        private void StartEncoding(int id)
        {
            // Sets Status of encode
            CurrentEncodingFileName = "Analyzing";
            IsEncoding = true;

            // Creates encode thread and starts it
            _thread = new Thread(() => EncodeThread(id));
            _thread.Start();
        }

        /// <summary>
        /// Stops the encode in progress and closes thread.
        /// </summary>
        public void StopEncoding()
        {
            cancelEncode = true;
            _thread.Join();
        }

        private readonly Job _encodeJob = new Job();

        private void EncodeThread(int id)
        {
            var tempPath = FilepathHelper.GetTempDirectoryPath(id);

            var files = new List<string>(Directory.GetFiles(tempPath, "*.xesc"));

            // TODO : metterli in ordine di data..

            if (files.Count == 0) return;

            MediaItem mediaItem;
            try
            {
                mediaItem = new MediaItem(files[0]) { };
                mediaItem.OutputFormat.VideoProfile.FrameRate = 5;
                mediaItem.OutputFormat.VideoProfile.Size = new System.Drawing.Size(640, 480);

            }
            catch (InvalidMediaFileException exp)
            {
                CurrentEncodingFileName = exp.Message;
                IsEncoding = false;
                return;
            }

            // Create the job, add the media item and encode.
            _encodeJob.ApplyPreset(Presets.VC1ScreenEncodingVBR);

            _encodeJob.MediaItems.Add(mediaItem);
            foreach (var file in files)
            {
                try
                {
                    mediaItem.Sources.Add(new Source(file));
                }
                catch (Exception ex)
                {
                }
            }

            _encodeJob.OutputDirectory = FilepathHelper.GetTempDirectoryPathResult(id);
            _encodeJob.EncodeProgress += OnProgress;
            _encodeJob.StartFileEncoding += job_StartFileEncoding;
            _encodeJob.EncodeCompleted += JobEncodeCompleted;

            CurrentEncodingFileName = "Encoding";
            _encodeJob.Encode();
            CurrentEncodingFileName = "Finished.";
            IsEncoding = false;
        }

        public void PauseEncoding()
        {
            if (IsEncoding)
                _encodeJob.PauseEncode();
        }
        public void ContinueEncoding()
        {
            _encodeJob.ResumeEncode();
        }


        /// <summary>
        /// Terminato encoding
        /// </summary>
        void JobEncodeCompleted(object sender, EncodeCompletedEventArgs e)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                new Action(() =>
                 {
                     var c = _queueWorkList.Currentencoding;
                     _queueWorkList.AddUpload(c);

                     TryStartUploading();
                 }));
        }

        /// <summary>
        /// Ha cominciato nuovo file
        /// </summary>
        void job_StartFileEncoding(object sender, StartFileEncodingEventArgs e)
        {

        }

        /// <summary>
        /// Called when encoding progress occurs.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event arguments.</param>
        private void OnProgress(object sender, EncodeProgressEventArgs e)
        {
            // If we should cancel then indcate that.
            if (cancelEncode)
            {
                e.Cancel = true;
                IsEncoding = true;
                return;
            }

            EncodingProgress = e.Progress;
            //Status = string.Format("{0:F1}%", e.Progress);
        }
        #endregion

        #region login upload
        private string _currentUploadFileName;
        public string CurrentUploadFileName
        {
            get { return _currentUploadFileName; }
            set { _currentUploadFileName = value; NotifyOfPropertyChange(() => CurrentUploadFileName); }
        }



        ///// <summary>
        ///// Todo : controllare limite 10 min se è riferito solamente a registrazione oppure a encoding
        ///// </summary>
        ///// <param name="filepath"></param>
        ///// <param name="username"></param>
        ///// <param name="password"></param>
        //void StartUploadVideo(string filepath, string username, string password)
        //{
        //    PathFolderHelper.UpdateStatus(SelectedExercise, LocalExerciseStatus.ToUpload);

        //    var webClient = new WebClientEx { Credentials = new NetworkCredential(username, password) };
        //    var values = new NameValueCollection
        //    {
        //        { "username", username },
        //        { "password", password},
        //    };


        //    // Login
        //    CurrentUploadFileName = "Autentication";

        //    var response = webClient.UploadValues("http://localhost:54419/Account/Login", values);

        //    var json = Encoding.UTF8.GetString(response);

        //    // todo _ controlla nella response se autenticazione e ok oppure no

        //    //var query = new NameValueCollection { { "field1", "test" } };
        //    //webClient.QueryString = query;

        //    webClient.UploadProgressChanged += WebClientUploadProgressChanged;
        //    webClient.UploadFileCompleted += WebClientUploadFileCompleted;
        //    webClient.UploadFileAsync(new Uri("http://localhost:54419/Tester/ExerciseVideo/"), filepath);
        //}
        //void WebClientUploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
        //{
        //    CurrentUploadFileName = "Completed";
        //    /*
        //     * - Setto video consegnato su sito.
        //     * - Cancello cartella temporanea.
        //     */
        //    TryStartUploading();
        //}

        //void WebClientUploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
        //{
        //    UploadProgress = e.ProgressPercentage;
        //

        /// <summary>
        /// Todo : controllare limite 10 min se è riferito solamente a registrazione oppure a encoding
        /// </summary>
        /// <param name="filepath"></param>
        void StartUploadVideo(string filepath)
        {
            var rslt = YouTubeUploader.AddVideo(filepath);
        }

        //private UploadItem _uploadItem;
        //public UploadItem CurrentUploadItem
        //{
        //    get { return _uploadItem; }
        //    set { _uploadItem = value; NotifyOfPropertyChange(() => _uploadItem); }
        //}



        #endregion
    }
}

